package HeartsCard;

import java.util.*;
import java.util.concurrent.*;

public class Playing extends Observable {

	private ArrayList<Integer> bottom;
	private ArrayList<Integer> left;
	private ArrayList<Integer> top;
	private ArrayList<Integer> right;
	
	private ConcurrentLinkedQueue<String> turning;//who waiting to play each trick
	private ConcurrentLinkedQueue<String> turned;//who played each trick
	private ArrayList<Integer> cardTurning;//card used each trick
	
	private int totalA = 0;
	private int pointA;
	
	private int totalB = 0;
	private int pointB;
	
	private int totalC = 0;
	private int pointC;
	
	private int totalD = 0;
	private int pointD;
	
	private boolean heart;//previous trick started by heart
	
	public Playing(Observer Object) {
		this.addObserver(Object);
		this.startNewRound();
	}
	
	public void startNewRound() {
		
		// new instances
		this.bottom = new ArrayList<Integer>();
		this.left = new ArrayList<Integer>();
		this.top = new ArrayList<Integer>();
		this.right = new ArrayList<Integer>();
		this.turned = new ConcurrentLinkedQueue<String>();
		this.turning = new ConcurrentLinkedQueue<String>();
		this.cardTurning = new ArrayList<Integer>();
		this.plusTotalA(this.getPointA());
		this.plusTotalB(this.getPointB());
		this.plusTotalC(this.getPointC());
		this.plusTotalD(this.getPointD());
		this.setPointA(0);
		this.setPointB(0);
		this.setPointC(0);
		this.setPointD(0);
		this.heart = false;
		
		// random 52 number
		ArrayList<Integer> data = new ArrayList<Integer>();
		for(int i = 0; i < 52; ++i)
			data.add(i);
		
		while(data.size() > 0) {
			Random rand = new Random();
			this.getBottom().add(data.remove(Math.abs(rand.nextInt()) % data.size()));
			this.getLeft().add(data.remove(Math.abs(rand.nextInt()) % data.size()));
			this.getTop().add(data.remove(Math.abs(rand.nextInt()) % data.size()));
			this.getRight().add(data.remove(Math.abs(rand.nextInt()) % data.size()));
		}
		
		// set first trick
		int result = this.getIndexOf(this.bottom, 13);
		if(result >= 0) {
			this.turning.add("bottom");
			this.turning.add("left");
			this.turning.add("top");
			this.turning.add("right");
			return;
		}

		result = this.getIndexOf(this.left, 13);
		if(result >= 0) {
			this.turning.add("left");
			this.turning.add("top");
			this.turning.add("right");
			this.turning.add("bottom");
			return;
		}
		
		result = this.getIndexOf(this.top, 13);
		if(result >= 0) {
			this.turning.add("top");
			this.turning.add("right");
			this.turning.add("bottom");
			this.turning.add("left");
			return;
		}
		
		result = this.getIndexOf(this.right, 13);
		if(result >= 0) {
			this.turning.add("right");
			this.turning.add("bottom");
			this.turning.add("left");
			this.turning.add("top");
			return;
		}
	}
	
	protected int getIndexOf(ArrayList<Integer> data, int key) {
		for(int i = 0; i< data.size(); ++i)
			if(data.get(i) == key) return i;
		return -1;
	}
	
	public void humanPlay(int indexChosen) {
		this.cardTurning.add(indexChosen);
		this.turned.add(this.turning.poll());
		this.bottom.remove(this.getIndexOf(this.bottom, indexChosen));
		//if(this.bottom.isEmpty())
		this.nextPlay();
	}
	
	public void nextPlay() {
		if (this.turning.isEmpty()) {
			this.calculate();
		}
		else if(this.turning.peek() != "bottom") {
			if(this.left.isEmpty() && this.top.isEmpty() && this.right.isEmpty()) {
				this.setChanged();
				if(this.checkNewRound()) {
					this.notifyObservers(true);
				}
				else {
					this.notifyObservers(false);
				}
				return;
			}
			
			String s = this.turning.peek();
			int randValue = 0;
			if(s == "left") {
				randValue = this.random(this.getAble(this.left));
				this.left.remove(this.getIndexOf(this.left, randValue));
			}
			else if(s == "top") {
				randValue = this.random(this.getAble(this.top));
				this.top.remove(this.getIndexOf(this.top, randValue));
			}
			else if(s == "right") {
				randValue = this.random(this.getAble(this.right));
				this.right.remove(this.getIndexOf(this.right, randValue));
			}
			this.cardTurning.add(randValue);
			this.turned.add(this.turning.poll());

			this.setChanged();
			this.notifyObservers(s + " " + randValue);
		}
		else {//turn of human
			if(this.bottom.isEmpty()) { 
				this.setChanged();
				if(this.checkNewRound())
					this.notifyObservers(true);
				else this.notifyObservers(false);
				return;
			}
			this.setChanged();
			this.notifyObservers(this.getAble(this.bottom));
		}
	}
	
	protected void calculate() {
		
		int first = this.cardTurning.get(0);
		int point = Card.getPoint(first);
		int go = 0;
		for(int i = 1; i < 4; ++i) {
			int x = Card.compare(first, this.cardTurning.get(i));
			switch(x) {
				case 1: break;
				//case 0: break;
				case -1:
					go = i;
					first = this.cardTurning.get(i);
					break;
				case -2: break;
			}
			point += Card.getPoint(this.cardTurning.get(i));
		}

		// next trick
		while(!this.turned.isEmpty())
			this.turning.add(this.turned.poll());
		
		// sets first 
		for(int i = 0; i < go; ++i) {
			this.turning.add(this.turning.poll());
		}
		
		if(Card.isHeart(this.cardTurning.get(0)))
			this.heart = true;
		else this.heart = false;
		
		int who = 0;
		if(this.turning.peek() == "left") who = 1;
		else if(this.turning.peek() == "top") who = 2;
		else if(this.turning.peek() == "right") who = 3;
		
		int isShootingTheMoon = 0;
		if(who == 0) {
			this.setPointA(this.getPointA() + point);
			if(this.getPointA() == 26) {
				isShootingTheMoon = 1;
				this.setPointA(0);
				this.setPointB(26);
				this.setPointC(26);
				this.setPointD(26);
			}
		}
		else if(who == 1) {
			this.setPointB(this.getPointB() + point);
			if(this.getPointB() == 26) {
				isShootingTheMoon = 1;
				this.setPointA(26);
				this.setPointB(0);
				this.setPointC(26);
				this.setPointD(26);
			}
		}
		else if(who == 2) {
			this.setPointC(this.getPointC() + point);
			if(this.getPointC() == 26) {
				isShootingTheMoon = 1;
				this.setPointA(26);
				this.setPointB(26);
				this.setPointC(0);
				this.setPointD(26);
			}
		}
		else if(who == 3) {
			this.setPointD(this.getPointD() + point);
			if(this.getPointD() == 26) {
				isShootingTheMoon = 1;
				this.setPointA(26);
				this.setPointB(26);
				this.setPointC(26);
				this.setPointD(0);
			}
		}
		
		this.setChanged();
		this.notifyObservers(new Integer[] {who, first, point, isShootingTheMoon});
	}
	
	protected ArrayList<Integer> getAble(ArrayList<Integer> cards) {
		int target = -1;
		if(!this.cardTurning.isEmpty())
			target = this.cardTurning.get(0);
		ArrayList<Integer> result = new ArrayList<Integer>();
		if(target == -1) {
			if(cards.size() == 13)//the first turn in round must uses the club two card
				result.add(new Integer(13));
			else {
				if(!this.heart) {//cannot use heart card 
					for(int i=0;i<cards.size();++i) {
						if(!Card.isHeart(cards.get(i)))
							result.add(new Integer(cards.get(i)));
					}
					if(result.size() > 0) return result;
				}
				
				//can use heart card because only have heart card
				for(int i=0;i<cards.size();++i)
					result.add(new Integer(cards.get(i)));
			}
			return result;
		}
		
		for(int i=0;i<cards.size();++i) {
			if(Card.isSameType(target, cards.get(i)))
				result.add(new Integer(cards.get(i)));
		}
		
		if(result.isEmpty()) {//when not any cards same type
			for(int i=0;i<cards.size();++i) {
				if(cards.size() == 13 && 
						(cards.get(i) == 10 || 
						Card.isHeart(cards.get(i)))
				   );
				else
					result.add(new Integer(cards.get(i)));
			}
			if(result.isEmpty()) {
				for(int i=0;i<cards.size();++i)
					result.add(new Integer(cards.get(i)));
			}
		}
		return result;
	}
	
	protected int random(ArrayList<Integer> data) {
		if(data.isEmpty()) return -1;
		int x = Math.abs(new Random().nextInt()) % data.size();
		return data.get(x);
	}
	
	protected boolean checkNewRound() {
		if(this.getTotalA() >= 100) return false;
		if(this.getTotalB() >= 100) return false;
		if(this.getTotalC() >= 100) return false;
		if(this.getTotalD() >= 100) return false;
		return true;
	}
	
	protected void plusTotalA(int value) {
		this.totalA += value;
	}

	public int getTotalA() {
		return totalA;
	}

	protected void setPointA(int pointA) {
		this.pointA = pointA;
	}

	public int getPointA() {
		return pointA;
	}

	protected void plusTotalB(int value) {
		this.totalB += value;
	}

	public int getTotalB() {
		return totalB;
	}

	protected void setPointB(int pointB) {
		this.pointB = pointB;
	}

	public int getPointB() {
		return pointB;
	}

	protected void plusTotalC(int value) {
		this.totalC += value;
	}

	public int getTotalC() {
		return totalC;
	}

	protected void setPointC(int pointC) {
		this.pointC = pointC;
	}

	public int getPointC() {
		return pointC;
	}

	protected void plusTotalD(int value) {
		this.totalD += value;
	}

	public int getTotalD() {
		return totalD;
	}

	protected void setPointD(int pointD) {
		this.pointD = pointD;
	}

	public int getPointD() {
		return pointD;
	}

	protected void setBottom(ArrayList<Integer> bottom) {
		this.bottom = bottom;
	}

	public ArrayList<Integer> getBottom() {
		return bottom;
	}

	protected void setLeft(ArrayList<Integer> left) {
		this.left = left;
	}

	public ArrayList<Integer> getLeft() {
		return left;
	}

	protected void setTop(ArrayList<Integer> top) {
		this.top = top;
	}

	public ArrayList<Integer> getTop() {
		return top;
	}

	protected void setRight(ArrayList<Integer> right) {
		this.right = right;
	}

	public ArrayList<Integer> getRight() {
		return right;
	}

	protected void setCardTurning(ArrayList<Integer> cardTurning) {
		this.cardTurning = cardTurning;
	}

	public ArrayList<Integer> getCardTurning() {
		return cardTurning;
	}
}
